/**
 *****************************************************************************
 * DESC ：. 
 * Author : Junwei Zhang 
 * CREATEDATE ：2012-7-27 
 * MODIFYLIST ：       	Name 			 Date 			Reason/Contents
 * 				------------------------------------------------------ 
 * 				 	Junwei Zhang     2012-7-27		Created.
 ****************************************************************************
 **/
package com.me.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.JFileChooser;

/**
 * 文件和字符解析类. <br/>
 * 上午8:23:11 2012-7-27
 * 
 * @Author <a href="mailto:mestudying@sina.com">Junwei Zhang</a><br/>
 **/
public class Base64Handler {

	public class EncodedFileObject {

		public String data = null;
		public String fileName = null;
		
		public EncodedFileObject(String fileName, String data) {
			this.data = data;
			this.fileName = fileName;
		}

	}

	/** Max file size = 300K. */
	private static final int MAX_FILE_SIZE = 1024 * 300;

	private static final String cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/";

	private static final int fillChar = '=';

	public String lastError = null;
	private JTalkChat parentFrame = null;

	public Base64Handler(JTalkChat parentFrame) {
		// TODO Auto-generated constructor stub
		this.parentFrame = parentFrame;
	}

	public EncodedFileObject getEncodedFile() {
		JFileChooser chooser = new JFileChooser();
		int retVal = chooser.showOpenDialog(this.parentFrame);
		if (retVal == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();
			try {
				int fileSize = (int) (new FileInputStream(file).getChannel().size());
				if (fileSize > MAX_FILE_SIZE) {
					this.lastError = "File is too big!Max file size is 300KB.";
					return null;
				}
				return new EncodedFileObject(file.getName(), Base64Handler.getString(Base64Handler
						.encode(getBytesFromFile(file))));
			} catch (Exception e) {
				e.printStackTrace();
				this.lastError = "Can't load/encode file: " + e.getMessage();
				return null;
			}
		}
		return null;
	}

	private static String getString(byte[] data) {
		StringBuffer str = new StringBuffer();
		for(byte b : data){
			str.append((char)b);
		}
		return str.toString();
	}

	private static byte[] encode(byte[] data) {
		int c = 0;
		int len = data.length;
		StringBuffer ret = new StringBuffer((len / 3 + 1) * 4);
		for(int i = 0; i < len; ++i){
			c = (data[i] >> 2) & 0x3f;
			ret.append(cvt.charAt(c));
			c = (data[i] << 4) & 0x3f;
			if(++i < len){
				c |= (data[i] >> 4) & 0x0f;
			}
			ret.append(cvt.charAt(c));
			if(i < len){
				c = (data[i] << 2) & 0x3f;
				if(++i < len){
					c |= (data[i] >> 6) & 0x03;
				}
				ret.append(cvt.charAt(c));
			}else{
				++i;
				ret.append((char) fillChar);
			}
			if(i < len){
				c = data[i] & 0x3f;
				ret.append(cvt.charAt(c));
			}else{
				ret.append((char) fillChar);
			}
		}
		return getBinaryBytes(ret.toString());
	}

	/**
	 * 将字符串转换成字节数组.
	 * @param str
	 * @return
	 */
	private static byte[] getBinaryBytes(String str) {
		byte[] b = new byte[str.length()];
		for(int i = 0; i < b.length; ++i){
			b[i] = (byte)str.charAt(i);
		}
		return b;
	}

	/**
	 * 将文件内容转化为字节.
	 * @param file
	 * @return
	 * @throws IOException
	 */
	private static byte[] getBytesFromFile(File file) throws IOException {
		InputStream in = new FileInputStream(file);
		long length = file.length();
		if(length > Integer.MAX_VALUE){
			return null;
		}
		byte[] bytes = new byte[(int)length];
		int offset = 0;
		int numRead = 0;
		while(offset < bytes.length && (numRead = in.read(bytes, offset, bytes.length - offset)) > 0){
			offset += numRead;
		}
		if(offset < bytes.length){
			throw new IOException("Can't completely read file " + file.getName());
		}
		in.close();
		return bytes;
	}

	public String getLastError() {
		return this.lastError;
	}

	/**
	 * 将字符串保存为一个文件.
	 * @param data 字符串.
	 * @param subject 文件名.
	 * @return true-保存成功,false-保存失败.
	 */
	public boolean saveDecodedData(String data, String fileName) {
		if(data == null || data.equals("")){
			this.lastError = "No data found";
			System.out.println("--------saveDecodedData-------" + Thread.currentThread() + "-" + this.lastError);
			return false;
		}
		if(fileName == null){
			fileName = "Unknown";
		}
		System.out.println("--------saveDecodedData1-------" + Thread.currentThread());
		JFileChooser chooser = new JFileChooser();
		System.out.println("--------saveDecodedData2-------" + Thread.currentThread());
		chooser.setDialogType(JFileChooser.SAVE_DIALOG);
		System.out.println("--------saveDecodedData3-------" + Thread.currentThread());
		chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		System.out.println("--------saveDecodedData4-------" + Thread.currentThread());
		chooser.setSelectedFile(new File(fileName));
		System.out.println("--------saveDecodedData5-------" + Thread.currentThread());
		int retVal = chooser.showSaveDialog(this.parentFrame);
		System.out.println("--------saveDecodedData6-------" + Thread.currentThread());
		if(retVal == JFileChooser.APPROVE_OPTION){
			File file = chooser.getSelectedFile();
			FileOutputStream os = null;
			try {
				os = new FileOutputStream(file);
				os.write(Base64Handler.decode(Base64Handler.getBinaryBytes(data)));
				os.close();
				return true;
			} catch (IOException e) {
				e.printStackTrace();
				this.lastError = "Can't save/decode file: " + e.getMessage();
				return false;
			}
		}
		this.lastError = "Action cancelled.";
		return false;
	}

	/**
	 * Decode data to a Base64 byte array.
	 * @param data
	 * @return
	 */
	private static byte[] decode(byte[] data) {
		// TODO Auto-generated method stub
		int c = 0;
		int c1 = 0;
		int len = data.length;
		StringBuffer ret = new StringBuffer((len * 3) / 4);
		for(int i = 0; i < len; ++i){
			c = cvt.indexOf(data[i]);
			++i;
			c1 = cvt.indexOf(data[i]);
			c = ((c << 2) | ((c1 >> 4) & 0x3));
			ret.append((char)c);
			if(++i < len){
				c = data[i];
				if(c == fillChar){
					break;
				}
				c = cvt.indexOf((char) c);
				c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
				ret.append((char) c1);
			}
			if(++i < len){
				c1 = data[i];
				if(fillChar == c1){
					break;
				}
				c1 = cvt.indexOf((char) c1);
				c = ((c << 6) & 0xc0) | c1;
				ret.append((char) c);
			}
		}
		return getBinaryBytes(ret.toString());
	}

}
